﻿/*
* Copyright (c) 2010-2012 Tesla Engine Group
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

using System;
using System.IO;
using Tesla.Graphics;
using Tesla.Math;

#pragma warning disable 1591

namespace Tesla.Content.Loaders {
	public sealed class MaterialSerializer {
        private TextWriter _writer;

        private const String TAB_BLOCK0 = "";
        private const String TAB_BLOCK1 = "\t";
        private const String TAB_BLOCK2 = "\t\t";
        private const String TAB_BLOCK3 = "\t\t\t";

        public String WriteTest() {
            _writer = new StringWriter();
            _writer.Write(TAB_BLOCK1);
            _writer.Write("MaterialParameters ");
            WriteOpenBrace();
            WriteSingle("radius", .5f);
            WriteInt("num", 2);
            WriteBool("UseSpecular", true);
            WriteVector2("TexCoords", new Vector2(.5f, .85f));
            WriteVector3("MatDiffuse", new Vector3(.5f, .5f, .25f));
            WriteVector4("spin", new Vector4(5.0f, 2.0f, 1.0f, 3.0f));
            WriteQuaternion("rot", Quaternion.FromAngleAxis(25, Vector3.Up));
            WriteMatrix("worldMat", Matrix.FromAngleAxis(2, Vector3.Down));
            WriteColor("emissive", Color.Plum);
            WriteCloseBrace(TAB_BLOCK1);
            return _writer.ToString();
        }

        public String RenderStateTest() {
            _writer = new StringWriter();
            _writer.Write("Material");
            WriteSpace();
            _writer.Write("RenderStateTest");
            WriteOpenBrace();
            _writer.Write(TAB_BLOCK1);
            _writer.Write("RenderStates");
            WriteOpenBrace();

            WriteBlendState("bs1", BlendState.AdditiveBlend);
            WriteBlendState("bs2", BlendState.AlphaBlendNonPremultiplied);
            WriteBlendState("bs3", BlendState.AlphaBlendPremultiplied);
            WriteBlendState("bs4", BlendState.Opaque);

            BlendState bs = new BlendState();
            bs.AlphaBlendFunction = BlendFunction.Max;
            bs.AlphaDestinationBlend = Blend.One;
            bs.AlphaSourceBlend = Blend.InverseSourceAlpha;
            bs.MultiSampleMask = 5;
            bs.BlendFactor = Color.Plum;
            bs.SetWriteChannels(1, ColorWriteChannels.Red | ColorWriteChannels.Blue | ColorWriteChannels.Green);
            WriteBlendState("custom", bs);

            WriteCloseBrace(TAB_BLOCK1);
            WriteCloseBrace(TAB_BLOCK0);

            return _writer.ToString();
        }

        private void WriteOpenBrace() {
            _writer.Write(" {\n");
        }

        private void WriteCloseBrace(String tabs) {
            WriteCloseBrace(true, tabs);
        }

        private void WriteCloseBrace(bool newLine, String tabs) {
            if(newLine) {
                WriteNewLine();
            }
            _writer.Write(tabs);
            _writer.Write("}\n");
        }

        private void WriteSeparator() {
            _writer.Write(" : ");
        }

        private void WriteSpace() {
            _writer.Write(' ');
        }

        private void WriteNewLine() {
            _writer.Write('\n');
        }

        #region Parameter Writing

        private void WriteSingle(String name, float value) {
            _writer.Write(TAB_BLOCK2);
            _writer.Write("float");
            WriteSpace();
            _writer.Write(name);
            WriteSeparator();
            _writer.Write(value);
            WriteNewLine();
        }

        private void WriteInt(String name, int value) {
            _writer.Write(TAB_BLOCK2);
            _writer.Write("int");
            WriteSpace();
            _writer.Write(name);
            WriteSeparator();
            _writer.Write(value);
            WriteNewLine();
        }

        private void WriteBool(String name, bool value) {
            _writer.Write(TAB_BLOCK2);
            _writer.Write("bool");
            WriteSpace();
            _writer.Write(name);
            WriteSeparator();
            _writer.Write(value ? "true" : "false");
            WriteNewLine();
        }

        private void WriteVector2(String name, Vector2 value) {
            _writer.Write(TAB_BLOCK2);
            _writer.Write("Vector2");
            WriteSpace();
            _writer.Write(name);
            WriteSeparator();
            _writer.Write(value.X);
            WriteSpace();
            _writer.Write(value.Y);
            WriteNewLine();
        }

        private void WriteVector3(String name, Vector3 value) {
            _writer.Write(TAB_BLOCK2);
            _writer.Write("Vector3");
            WriteSpace();
            _writer.Write(name);
            WriteSeparator();
            _writer.Write(value.X);
            WriteSpace();
            _writer.Write(value.Y);
            WriteSpace();
            _writer.Write(value.Z);
            WriteNewLine();
        }

        private void WriteVector4(String name, Vector4 value) {
            _writer.Write(TAB_BLOCK2);
            _writer.Write("Vector4");
            WriteSpace();
            _writer.Write(name);
            WriteSeparator();
            _writer.Write(value.X);
            WriteSpace();
            _writer.Write(value.Y);
            WriteSpace();
            _writer.Write(value.Z);
            WriteSpace();
            _writer.Write(value.W);
            WriteNewLine();
        }

        private void WriteQuaternion(String name, Quaternion value) {
            _writer.Write(TAB_BLOCK2);
            _writer.Write("Quaternion");
            WriteSpace();
            _writer.Write(name);
            WriteSeparator();
            _writer.Write(value.X);
            WriteSpace();
            _writer.Write(value.Y);
            WriteSpace();
            _writer.Write(value.Z);
            WriteSpace();
            _writer.Write(value.W);
            WriteNewLine();
        }

        private void WriteMatrix(String name, Matrix value) {
            _writer.Write(TAB_BLOCK2);
            _writer.Write("Matrix");
            WriteSpace();
            _writer.Write(name);
            WriteSeparator();
            for(int i = 1; i <= 4; i++) {
                for(int j = 1; j <= 4; j++) {
                    _writer.Write(value[i, j]);
                    WriteSpace();
                }
            }
            WriteNewLine();
        }

        private void WriteColor(String name, Color value) {
            _writer.Write(TAB_BLOCK2);
            _writer.Write("Color");
            WriteSpace();
            _writer.Write(name);
            WriteSeparator();
            _writer.Write(value.R);
            WriteSpace();
            _writer.Write(value.G);
            WriteSpace();
            _writer.Write(value.B);
            WriteSpace();
            _writer.Write(value.A);
            WriteNewLine();
        }

        #endregion

        #region RenderState writing

        private void WriteBlendState(String name, BlendState value) {
            _writer.Write(TAB_BLOCK2);
            _writer.Write("BlendState");
            WriteSpace();
            _writer.Write(name);
            RenderStateKey key = value.RenderStateKey;
            if(key.Equals(BlendState.AdditiveBlend.RenderStateKey)) {
                WriteSeparator();
                _writer.Write("AdditiveBlend");
                WriteNewLine();
            } else if(key.Equals(BlendState.AlphaBlendNonPremultiplied.RenderStateKey)) {
                WriteSeparator();
                _writer.Write("AlphaBlendNonPremultiplied");
                WriteNewLine();
            } else if(key.Equals(BlendState.AlphaBlendPremultiplied.RenderStateKey)) {
                WriteSeparator();
                _writer.Write("AlphaBlendPremultiplied");
                WriteNewLine();
            } else if(key.Equals(BlendState.Opaque.RenderStateKey)) {
                WriteSeparator();
                _writer.Write("Opaque");
                WriteNewLine();
            } else {
                WriteOpenBrace();

                if(value.AlphaBlendFunction != BlendFunction.Add) {
                    _writer.Write(TAB_BLOCK3);
                    _writer.Write("AlphaBlendFunction");
                    WriteSeparator();
                    _writer.Write(value.AlphaBlendFunction.ToString());
                    WriteNewLine();
                }

                if(value.AlphaSourceBlend != Blend.One) {
                    _writer.Write(TAB_BLOCK3);
                    _writer.Write("AlphaSourceBlend");
                    WriteSeparator();
                    _writer.Write(value.AlphaSourceBlend.ToString());
                    WriteNewLine();
                }

                if(value.AlphaDestinationBlend != Blend.Zero) {
                    _writer.Write(TAB_BLOCK3);
                    _writer.Write("AlphaDestinationBlend");
                    WriteSeparator();
                    _writer.Write(value.AlphaDestinationBlend.ToString());
                    WriteNewLine();
                }

                if(value.ColorBlendFunction != BlendFunction.Add) {
                    _writer.Write(TAB_BLOCK3);
                    _writer.Write("ColorBlendFunction");
                    WriteSeparator();
                    _writer.Write(value.ColorBlendFunction.ToString());
                    WriteNewLine();
                }

                if(value.ColorSourceBlend != Blend.One) {
                    _writer.Write(TAB_BLOCK3);
                    _writer.Write("ColorSourceBlend");
                    WriteSeparator();
                    _writer.Write(value.ColorSourceBlend.ToString());
                    WriteNewLine();
                }

                if(value.ColorDestinationBlend != Blend.Zero) {
                    _writer.Write(TAB_BLOCK3);
                    _writer.Write("ColorDestinationBlend");
                    WriteSeparator();
                    _writer.Write(value.ColorDestinationBlend.ToString());
                    WriteNewLine();
                }

                if(!value.BlendFactor.Equals(Color.White)) {
                    _writer.Write(TAB_BLOCK3);
                    _writer.Write("BlendFactor");
                    WriteSeparator();
                    Color c = value.BlendFactor;
                    _writer.Write(c.R);
                    WriteSpace();
                    _writer.Write(c.G);
                    WriteSpace();
                    _writer.Write(c.B);
                    WriteSpace();
                    _writer.Write(c.A);
                    WriteNewLine();
                }

                if(value.MultiSampleMask != int.MaxValue) {
                    _writer.Write(TAB_BLOCK3);
                    _writer.Write("MultiSampleMask");
                    WriteSeparator();
                    _writer.Write(value.MultiSampleMask);
                    WriteNewLine();
                }

                int beCount = value.BlendEnableCount;
                if(beCount > 0) {
                    if(value.GetBlendEnable(0) == false) {
                        _writer.Write(TAB_BLOCK3);
                        _writer.Write("BlendEnable[0]");
                        WriteSeparator();
                        _writer.Write("false");
                        WriteNewLine();
                    }

                    for(int i = 1; i < beCount; i++) {
                        if(value.GetBlendEnable(i) == true) {
                            _writer.Write(TAB_BLOCK3);
                            _writer.Write("BlendEnable[");
                            _writer.Write(i);
                            _writer.Write("]");
                            WriteSeparator();
                            _writer.Write("true");
                            WriteNewLine();
                        }
                    }
                }

                int cwCount = value.ColorWriteChannelsCount;
                if(cwCount > 0) {
                    ColorWriteChannels cwc = value.GetWriteChannels(0);
                    if(cwc != ColorWriteChannels.All) {
                        _writer.Write(TAB_BLOCK3);
                        _writer.Write("ColorWriteChannels[0]");
                        WriteSeparator();
                        _writer.Write(cwc.ToString().Replace(",", " |"));
                        WriteNewLine();
                    }

                    for(int i = 1; i < cwCount; i++) {
                        cwc = value.GetWriteChannels(i);
                        if(cwc != ColorWriteChannels.All) {
                            _writer.Write(TAB_BLOCK3);
                            _writer.Write("ColorWriteChannels[");
                            _writer.Write(i);
                            _writer.Write("]");
                            WriteSeparator();
                            _writer.Write(cwc.ToString().Replace(",", " |"));
                            WriteNewLine();
                        }
                    }
                }

               WriteCloseBrace(false, TAB_BLOCK2);
            }
        }

        private void WriteRasterizerState(String name, RasterizerState value) {
            _writer.Write(TAB_BLOCK2);
            _writer.Write("RasterizerState");
            WriteSpace();
            _writer.Write(name);
            RenderStateKey key = value.RenderStateKey;
            if(key.Equals(RasterizerState.CullBackClockwiseFront.RenderStateKey)) {
                WriteSeparator();
                _writer.Write("CullBackClockwiseFront");
                WriteNewLine();
            } else if(key.Equals(RasterizerState.CullBackCounterClockwiseFront.RenderStateKey)) {
                WriteSeparator();
                _writer.Write("CullBackCounterClockwiseFront");
                WriteNewLine();
            } else if(key.Equals(RasterizerState.CullNone.RenderStateKey)) {
                WriteSeparator();
                _writer.Write("CullNone");
                WriteNewLine();
            } else if(key.Equals(RasterizerState.CullNoneWireframe.RenderStateKey)) {
                WriteSeparator();
                _writer.Write("CullNoneWireframe");
                WriteNewLine();
            } else {
                WriteOpenBrace();

                if(value.Cull != CullMode.Back) {
                    _writer.Write(TAB_BLOCK3);
                    _writer.Write("Cull");
                    WriteSeparator();
                    _writer.Write(value.Cull.ToString());
                    WriteNewLine();
                }

                if(value.VertexWinding != VertexWinding.Clockwise) {
                    _writer.Write(TAB_BLOCK3);
                    _writer.Write("VertexWinding");
                    WriteSeparator();
                    _writer.Write(value.VertexWinding.ToString());
                    WriteNewLine();
                }

                if(value.DepthBias != 0) {
                    _writer.Write(TAB_BLOCK3);
                    _writer.Write("DepthBias");
                    WriteSeparator();
                    _writer.Write(value.DepthBias);
                    WriteNewLine();
                }

                if(value.Fill != FillMode.Solid) {
                    _writer.Write(TAB_BLOCK3);
                    _writer.Write("Fill");
                    WriteSeparator();
                    _writer.Write(value.Fill.ToString());
                    WriteNewLine();
                }

                if(value.EnableMultiSampleAntiAlias == false) {
                    _writer.Write(TAB_BLOCK3);
                    _writer.Write("EnableMultiSampleAntiAlias");
                    WriteSeparator();
                    _writer.Write("false");
                    WriteNewLine();
                }

                if(value.EnableScissorTest == true) {
                    _writer.Write(TAB_BLOCK3);
                    _writer.Write("EnableScissorTest");
                    WriteSeparator();
                    _writer.Write("true");
                    WriteNewLine();
                }

                if(!value.SlopeScaledDepthBias.Equals(0.0f)) {
                    _writer.Write(TAB_BLOCK3);
                    _writer.Write("SlopeScaledDepthBias");
                    WriteSeparator();
                    _writer.Write(value.SlopeScaledDepthBias);
                    WriteNewLine();
                }

                WriteCloseBrace(false, TAB_BLOCK2);
            }
        }

        private void WriteDepthStencilState(String name, DepthStencilState value) {

        }

        private void WriteSamplerState(bool pixelSampler, int index, SamplerState value) {
            _writer.Write(TAB_BLOCK2);
            if(pixelSampler) {
                _writer.Write("SamplerState");
            } else {
                _writer.Write("VertexSamplerState");
            }
            _writer.Write("[");
            _writer.Write(index);
            _writer.Write("]");
            RenderStateKey key = value.RenderStateKey;
            if(key.Equals(SamplerState.AnisotropicClamp.RenderStateKey)) {
                WriteSeparator();
                _writer.Write("AnisotropicClamp");
                WriteNewLine();
            } else if(key.Equals(SamplerState.AnisotropicWrap.RenderStateKey)) {
                WriteSeparator();
                _writer.Write("AnisotropicWrap");
                WriteNewLine();
            } else if(key.Equals(SamplerState.LinearClamp.RenderStateKey)) {
                WriteSeparator();
                _writer.Write("LinearClamp");
                WriteNewLine();
            } else if(key.Equals(SamplerState.LinearWrap.RenderStateKey)) {
                WriteSeparator();
                _writer.Write("LinearWrap");
                WriteNewLine();
            } else if(key.Equals(SamplerState.PointClamp.RenderStateKey)) {
                WriteSeparator();
                _writer.Write("PointClamp");
                WriteNewLine();
            } else if(key.Equals(SamplerState.PointWrap.RenderStateKey)) {
                WriteSeparator();
                _writer.Write("PointWrap");
                WriteNewLine();
            } else {
                WriteOpenBrace();

                if(value.AddressU != TextureAddressMode.Clamp) {
                    _writer.Write(TAB_BLOCK3);
                    _writer.Write("AddressU");
                    WriteSeparator();
                    _writer.Write(value.AddressU.ToString());
                    WriteNewLine();
                }

                if(value.AddressV != TextureAddressMode.Clamp) {
                    _writer.Write(TAB_BLOCK3);
                    _writer.Write("AddressV");
                    WriteSeparator();
                    _writer.Write(value.AddressV.ToString());
                    WriteNewLine();
                }

                if(value.AddressW != TextureAddressMode.Clamp) {
                    _writer.Write(TAB_BLOCK3);
                    _writer.Write("AddressW");
                    WriteSeparator();
                    _writer.Write(value.AddressW.ToString());
                    WriteNewLine();
                }

                if(value.Filter != TextureFilter.Point) {
                    _writer.Write(TAB_BLOCK3);
                    _writer.Write("Filter");
                    WriteSeparator();
                    _writer.Write(value.Filter.ToString());
                    WriteNewLine();
                }

                if(value.MaxAnisotropy != 4) {
                    _writer.Write(TAB_BLOCK3);
                    _writer.Write("MaxAnisotropy");
                    WriteSeparator();
                    _writer.Write(value.MaxAnisotropy);
                    WriteNewLine();
                }

                if(!value.MipMapLevelOfDetailBias.Equals(0.0f)) {
                    _writer.Write(TAB_BLOCK3);
                    _writer.Write("MipMapLevelOfDetailBias");
                    WriteSeparator();
                    _writer.Write(value.MipMapLevelOfDetailBias);
                    WriteNewLine();
                }

                WriteCloseBrace(false, TAB_BLOCK2);
            }
        }

        #endregion
    }
}
